package de.hijacksoft.isle.adapters;

import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import de.hijacksoft.isle.classes.Item;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

public class SyncAdapter {

    public static final int STATUS_ERROR = -1;
    // STATUS
    public static final int STATUS_CONNECTING = 0;
    public static final int STATUS_CONNECTED = 1;
    public static final int STATUS_READ_STARTED = 2;
    public static final int STATUS_READ_FINISHED = 3;
    public static final int STATUS_WRITE_STARTED = 4;
    public static final int STATUS_WRITE_FINISHED = 5;
    public static final int STATUS_DISCONNECTED = 6;

    // USB COMMANDS
    private static final String CMD_CONNECTED = "CONNECTED";
    private static final String CMD_SEND_START = "SEND_START";
    private static final String CMD_SEND_FINISH = "SEND_FINISH";
    private static final String CMD_RECIEVE = "RECIEVE";
    private static final String CMD_CLOSE = "CLOSE";

    // DESKTOP TOOL VERSION
    private static final String DESKTOPTOOLVERSION = "100";
    
    private Handler handler = new Handler(Looper.getMainLooper());
    private Socket client;
    private BufferedReader in;
    private PrintWriter out;
    private DBAdapter adapter;
    private int status;
    private IdleThread idleThread;
    private static SyncAdapter instance = null;
    private ArrayList<Item> itemDB = null;
    private boolean isConnected = false;
    
    public static void initAdapter(Socket client, BufferedReader in, PrintWriter out, DBAdapter adapter){
        if(instance != null) instance.close();
        new SyncAdapter(client, in, out, adapter);
    }
    
    public boolean isConnected(){
        return isConnected;
    }
    
    public static SyncAdapter getInstance(){
        return instance;
    }
    
    private SyncAdapter(Socket client, BufferedReader in, PrintWriter out, DBAdapter adapter){
        this.client = client;
        this.in = in;
        this.out = out;
        this.adapter = adapter;
        writeLine(CMD_CONNECTED + DESKTOPTOOLVERSION);
        try {
            if(in.readLine().equals(CMD_CONNECTED)) isConnected = true;
        } catch (IOException ex) {
        }
        idleThread = new IdleThread();
        idleThread.start();
        instance = this;
    }
    
    public void writeLine(String line){
        out.write(line + "\n");
        out.flush();
    }
    
    public void close(){
        
        try {
            writeLine(CMD_CLOSE);
        } catch (Exception ex) {
        }
        
        try {
            client.close();
            setStatus(STATUS_DISCONNECTED);
        } catch (IOException ex) {
            Logger.getLogger(SyncAdapter.class.getName()).log(Level.SEVERE, null, ex);
        }
        
    }
    
    public ArrayList<Item> getDatabase(){
        return itemDB;
    }
    
    public int getStatus(){
        return status;
    }
    
    private void setStatus(int status){
        this.status = status;
        notifyListeners();
    }
    
    private void notifyListeners(){
        // notify on UI thread
        handler.post(new Thread(){
            
            @Override
            public void run(){
                for(StatusListener sl : listeners) sl.onStatusChange(status, instance);
            }
            
        });
    }
        
    private ArrayList<StatusListener> listeners = new ArrayList<StatusListener>();
    
    public void addStatusListener(StatusListener sl){
        listeners.add(sl);
    }
    
    public void removeStatusListener(StatusListener sl){
        listeners.remove(sl);
    }

    public void onProgressChange(Item currentItem, double progress){
        // callback
    }

    public void writeDatabase(){
        if(client != null && client.isConnected()){
            new WriteThread().start();
        }
    }
    
    public void readDatabase(){
        writeLine(CMD_RECIEVE);
    }
    
    private void startReadDatabase(){
        if(client != null && client.isConnected()){
            new ReadThread().start();
        }
    }
    
    /**
     * This thread waits for commands from the other device and handles them
     */
    class IdleThread implements Runnable {
        
        boolean close = false;
        boolean stopListening = false;
        
        private final Thread th;
        
        public void start(){
            th.start();
        }
        
        public IdleThread(){            
            th = new Thread(this);
        }
        
        @Override
        public void run() {

            while(!close){
                if(!stopListening){

                    try{

                        String line = in.readLine();

                        Log.d("USBCommand", line);
                        
                        if(line.equals(CMD_SEND_START)) startReadDatabase();
                        if(line.equals(CMD_RECIEVE)) writeDatabase();
                        if(line.equals(CMD_CLOSE)) close();
                        
                    }
                    catch(Exception e){
                    }

                }
                try {        
                    th.sleep(200);
                } catch (InterruptedException ex) {
                }

            }

        }

    }
    
    private class ReadThread extends Thread {
        
        @Override
        public void run(){
            
            idleThread.stopListening = true;
            setStatus(STATUS_READ_STARTED);
            
            ArrayList<Item> items = new ArrayList<Item>();        
            String line = null;

            try {
                while((line = in.readLine()) != null){
                    
                    if(line.equals(CMD_SEND_FINISH)) break;

                    Item addItem = Item.getItemFromString(line);

                    if(addItem != null) items.add(addItem);

                    if(line.equals("")) break;                    

                }
            } catch (IOException ex) {
                System.err.println("USBDataAdapter: I/O error " + ex.getStackTrace());
                setStatus(STATUS_ERROR);
            }
            
            itemDB = items;
            
            setStatus(STATUS_READ_FINISHED);
            idleThread.stopListening = false;
            
        }
        
    }
    
    private class WriteThread extends Thread {
        
        @Override
        public void run(){
            
            writeLine(CMD_SEND_START);

            setStatus(STATUS_WRITE_STARTED);
            idleThread.stopListening = true;
            
            ArrayList<Item> items = adapter.getAllItems();

            for(Item item : items){
                writeLine(item.toString());
            }        

            writeLine(CMD_SEND_FINISH);

            setStatus(STATUS_WRITE_FINISHED);
            idleThread.stopListening = false;
            
        }
        
    }
    
}
